home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000 #2
/
Ham Radio 2000 - Volume 2.iso
/
HAMV2
/
TCP_IP
/
TNOS230S
/
I8250.C
< prev
next >
Wrap
C/C++ Source or Header
|
1997-05-24
|
21KB
|
934 lines
#ifdef MSDOS
/* OS- and machine-dependent stuff for the 8250 asynch chip on a IBM-PC
* Copyright 1991 Phil Karn, KA9Q
*
* 16550A support plus some statistics added mah@hpuviea.at 15/7/89
*
* CTS hardware flow control from dkstevens@ucdavis,
* additional stats from delaroca@oac.ucla.edu added by karn 4/17/90
* Feb '91 RLSD line control reorganized by Bill_Simpson@um.cc.umich.edu
* Sep '91 All control signals reorganized by Bill Simpson
* Jan '92 Added force 16550 fifo command
*
* Mods by PA0GRI
*/
#include "global.h"
#include <dos.h>
#include "mbuf.h"
#include "proc.h"
#include "iface.h"
#include "i8250.h"
#include "asy.h"
#include "devparam.h"
#include "pc.h"
#include "kisspoll.h"
#include "timer.h"
#include "commands.h"
#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: i8250.c,v 1.15 1997/05/24 13:07:08 root Exp root $";
#endif
#ifdef TIPMAIL
extern struct suspended {
struct iface *ifp;
unsigned timeout;
char modem;
} Tipsuspended[ASY_MAX];
#include "cmdparse.h"
extern struct cmds Cmds[];
#endif
static void asy_monitor (int dev,void *p1,void *p2);
static void asy_output (int dev,char *buf,unsigned short cnt);
static int asyrxint (struct asy *asyp);
static void asytxint (int dev);
static void asymsint (int dev);
static void asy_tx (int dev,void *p1,void *p2);
struct asy Asy[ASY_MAX];
/* ASY interrupt handlers */
static void (*Handle[])() = {asy0vec,asy1vec,asy2vec,asy3vec,asy4vec};
static unsigned char fifo_setup;
/* Initialize asynch port "dev" */
int
asy_init(dev,ifp,arg1,arg2,bufsize,trigchar,monitor,speed,force,triglevel,polled)
int dev;
struct iface *ifp;
char *arg1,*arg2; /* Attach args for address and vector */
int16 bufsize;
int trigchar;
char monitor;
long speed;
int force;
int triglevel;
int polled;
{
register unsigned base;
register struct fifo *fp;
register struct asy *ap;
char *ifn;
char i_state;
long interval;
ap = &Asy[dev];
ap->iface = ifp;
ap->addr = htoi(arg1);
ap->vec = htoi(arg2);
#ifdef POLLEDKISS
ap->poller = NULL;
#endif
/* Set up receiver FIFO */
fp = &ap->fifo;
fp->buf = mallocw(bufsize);
fp->bufsize = bufsize;
fp->wp = fp->rp = fp->buf;
fp->cnt = 0;
fp->hiwat = 0;
fp->overrun = 0;
base = ap->addr;
ap->trigchar = trigchar;
/* Purge the receive data buffer */
(void)inportb(base+RBR);
i_state = disable ();
/* Save original interrupt vector, mask state, control bits */
ap->save.vec = getirq(ap->vec);
ap->save.mask = getmask(ap->vec);
ap->save.lcr = inportb(base+LCR);
ap->save.ier = inportb(base+IER);
ap->save.mcr = inportb(base+MCR);
ap->save.msr = inportb(base+MSR);
ap->save.iir = inportb(base+IIR);
/* save speed bytes */
setbit(base+LCR,LCR_DLAB);
ap->save.divl = inportb(base+DLL);
ap->save.divh = inportb(base+DLM);
clrbit(base+LCR,LCR_DLAB);
/* save some information on the starting state */
ap->cts_flow_control = (ap->save.msr & MSR_CTS) ? FOUND_UP : FOUND_DOWN;
ap->rlsd_line_control = (ap->save.msr & MSR_RLSD) ? FOUND_UP : FOUND_DOWN;
ap->dtr_usage = (ap->save.mcr & MCR_DTR) ? FOUND_UP : FOUND_DOWN;
ap->rts_usage = (ap->save.mcr & MCR_RTS) ? FOUND_UP : FOUND_DOWN;
/* Set interrupt vector to SIO handler */
setirq(ap->vec,Handle[dev]);
/* Set line control register: 8 bits, no parity */
outportb(base+LCR,(char)LCR_8BITS);
/* determine if 16550A, turn on FIFO mode and clear RX and TX FIFOs */
outportb(base+FCR,(char) FIFO_ENABLE);
/* According to National ap note AN-493, the FIFO in the 16550 chip
* is broken and must not be used. To determine if this is a 16550A
* (which has a good FIFO implementation) check that both bits 7
* and 6 of the IIR are 1 after setting the fifo enable bit. If
* not, don't try to use the FIFO.
*/
/* Set the fifo trigger level according to the user's wishes :-) - WG7J */
if(triglevel == 1)
fifo_setup = FIFO_SETUP1;
else if(triglevel == 4)
fifo_setup = FIFO_SETUP4;
else if(triglevel == 8)
fifo_setup = FIFO_SETUP8;
else if(triglevel == 14)
fifo_setup = FIFO_SETUP14;
else
fifo_setup = FIFO_SETUP4; /* default to 4 */
if (((inportb(base+IIR) & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED) || force) {
ap->is_16550a = TRUE;
outportb(base+FCR,fifo_setup);
} else {
/* Chip is not a 16550A. In case it's a 16550 (which
* has a broken FIFO), turn off the FIFO bit.
*/
outportb(base+FCR,(char)0);
ap->is_16550a = FALSE;
}
/* Turn on modem status and receive interrupts,
* leave transmit interrupts off for now.
*/
outportb(base+IER, (char)(IER_MS + IER_DAV));
/* Turn on 8250 master interrupt enable (connected to OUT2) */
setbit(base+MCR,(MCR_OUT2));
/* Enable interrupt */
maskon(ap->vec);
restore(i_state);
asy_speed(dev,speed);
if ( monitor ) {
/* set up to monitor line signals */
ap->monitor = newproc( ifn = if_name( ifp, " monitor" ),
256, asy_monitor, ifp->dev, ifp, NULL, 0);
free(ifn);
kwait(NULL); /* let hooks get set up */
}
ifp->txproc = newproc( ifn = if_name(ifp," tx"),
256, asy_tx, dev, ifp, NULL, 0);
free(ifn);
if ( (ap->dtr_usage & FOUND_UP)
&& (ap->rlsd_line_control & FOUND_UP)
|| ap->monitor == NULL ) {
if ( ifp->iostatus != NULL ) {
(*ifp->iostatus)(ifp, PARAM_UP, 0L);
}
}
#ifdef POLLEDKISS
if (polled) {
/* Calculate the poll interval: some processing time +
* the packet duration for a mtu size packet.
*/
interval = (((long)ifp->mtu * 10000L) / speed);
ap->poller = newproc (ifn=if_name(ifp," poller"),
384,kiss_poller,ifp->xdev,(void *)interval,NULL,0);
free (ifn);
}
#endif
return 0;
}
int
asy_stop(ifp)
struct iface *ifp;
{
register unsigned base;
register struct asy *ap;
char i_state;
ap = &Asy[ifp->dev];
#ifdef POLLEDKISS
if (ap->poller)
killproc (ap->poller);
#endif
if ( ap->monitor != NULL ) {
ap->rlsd_line_control = IGNORED;
killproc( ap->monitor );
ap->monitor = NULL;
}
ap->iface = NULLIF;
base = ap->addr;
/* Purge the receive data buffer */
(void)inportb(base+RBR);
/* and hardware fifos if available */
if (ap->is_16550a) {
if((ap->save.iir & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED)
outportb(base+FCR,fifo_setup);
else
outportb(base+FCR,0);
}
/* Restore original interrupt vector and 8259 mask state */
i_state = disable ();
setirq(ap->vec,ap->save.vec);
if(ap->save.mask)
maskon(ap->vec);
else
maskoff(ap->vec);
/* Restore speed regs */
setbit(base+LCR,LCR_DLAB);
outportb(base+DLL,ap->save.divl); /* Low byte */
outportb(base+DLM,ap->save.divh); /* Hi byte */
clrbit(base+LCR,LCR_DLAB);
/* Restore control regs */
/* except, leave DTR in current state {bsimpson} */
outportb(base+LCR,ap->save.lcr);
outportb(base+IER,ap->save.ier);
outportb(base+MCR,ap->save.mcr);
#ifdef notdef
outportb(base+MCR,(ap->save.mcr &~ MCR_DTR)
| (inportb(base+MCR) & MCR_DTR) );
#endif
restore(i_state);
free(ap->fifo.buf);
return 0;
}
/* Set asynch line speed */
int
asy_speed(dev,bps)
int dev;
long bps;
{
register unsigned base;
register long divisor;
struct asy *asyp;
char i_state;
if(bps <= 0 || dev >= ASY_MAX)
return -1;
asyp = &Asy[dev];
if(asyp->iface == NULLIF)
return -1;
switch(bps) {
case 300:
case 1200:
case 2400:
case 4800:
case 9600:
case 19200:
case 38400L:
case 56700L:
case 115200L:
/* supported speed */
asyp->speed = bps;
break;
default:
/* unsupported speed */
return -1;
};
base = asyp->addr;
divisor = BAUDCLK / bps;
i_state = disable ();
/* Purge the receive data buffer */
(void)inportb(base+RBR);
if (asyp->is_16550a) /* clear tx+rx fifos */
outportb(base+FCR,fifo_setup);
/* Turn on divisor latch access bit */
setbit(base+LCR,LCR_DLAB);
/* Load the two bytes of the register */
outportb(base+DLL,(char)(divisor & 0xff)); /* Low byte */
outportb(base+DLM,(char)((divisor >> 8) & 0xff)); /* Hi byte */
/* Turn off divisor latch access bit */
clrbit(base+LCR,LCR_DLAB);
restore(i_state);
return 0;
}
/* Asynchronous line I/O control */
int32
asy_ioctl(ifp,cmd,set,val)
struct iface *ifp;
int cmd;
int set;
int32 val;
{
struct asy *ap = &Asy[ifp->dev];
int16 base = ap->addr;
switch(cmd){
case PARAM_SPEED:
if(set)
asy_speed(ifp->dev,val);
return ap->speed;
case PARAM_DTR:
if(set) {
writebit(base+MCR,MCR_DTR,(int)val);
ap->dtr_usage = (val) ? MOVED_UP : MOVED_DOWN;
}
return (inportb(base+MCR) & MCR_DTR) ? TRUE : FALSE;
case PARAM_RTS:
if(set) {
writebit(base+MCR,MCR_RTS,(int)val);
ap->rts_usage = (val) ? MOVED_UP : MOVED_DOWN;
}
return (inportb(base+MCR) & MCR_RTS) ? TRUE : FALSE;
case PARAM_DOWN:
clrbit(base+IER,(char)IER_DAV);
clrbit(base+MCR,MCR_RTS);
clrbit(base+MCR,MCR_DTR);
ap->rts_usage = MOVED_DOWN;
ap->dtr_usage = MOVED_DOWN;
return FALSE;
case PARAM_UP:
setbit(base+IER,(char)IER_DAV);
setbit(base+MCR,MCR_RTS);
setbit(base+MCR,MCR_DTR);
ap->rts_usage = MOVED_UP;
ap->dtr_usage = MOVED_UP;
return TRUE;
case PARAM_BLIND:
setbit(base+IER,(char)IER_DAV);
ap->rlsd_line_control = IGNORED;
if ( ap->monitor != NULL ) {
killproc( ap->monitor );
ap->monitor = NULL;
}
/* can't see what we are doing, so pretend we're up */
if ( ifp->iostatus != NULL ) {
(*ifp->iostatus)(ifp, PARAM_UP, 0L);
}
return TRUE;
};
return -1;
}
/* Start transmission of a buffer on the serial transmitter */
static void
asy_output(dev,buf,cnt)
int dev;
char *buf;
unsigned short cnt;
{
register struct dma *dp;
unsigned base;
char i_state;
char ier;
struct asy *asyp;
if(dev < 0 || dev >= ASY_MAX)
return;
asyp = &Asy[dev];
if(asyp->iface == NULLIF)
return;
base = asyp->addr;
dp = &asyp->dma;
i_state = disable ();
if(dp->flags){
restore(i_state);
return; /* Already busy */
}
dp->data = buf;
dp->cnt = cnt;
dp->flags = 1;
if(asyp->cts_flow_control & MOVED_DOWN){
/* CTS flow control is enabled; let the modem control
* interrupt enable transmit interrupts if CTS is off
*/
ier = IER_MS;
if(inportb(base+MSR) & MSR_CTS)
ier |= IER_TxE;
} else {
/* Enable transmit interrupts; this will cause an immediate
* interrupt that will start things going
*/
ier = IER_TxE;
}
setbit(base+IER,ier);
/* "Kick start" the transmitter interrupt routine, in case just
* setting the interrupt enable bit doesn't cause an interrupt
*/
if(ier & IER_TxE)
asytxint(dev);
restore(i_state);
}
/* Blocking read from asynch line
* Returns character or -1 if aborting
* Returns -2 (CD_DOWN) if carrier checking is on and dropped
*/
int
get_asy(dev)
int dev;
{
char i_state;
register struct fifo *fp;
char c;
fp = &Asy[dev].fifo;
i_state = disable ();
while(fp->cnt == 0){
if(kwait(fp) != 0){
restore(i_state);
return -1;
}
}
fp->cnt--;
restore(i_state);
c = *fp->rp++;
if(fp->rp >= &fp->buf[fp->bufsize])
fp->rp = fp->buf;
return uchar(c);
}
/* Interrupt handler for 8250 asynch chip (called from asyvec.asm) */
void
asyint(dev)
int dev;
{
struct asy *asyp;
unsigned base;
char iir;
asyp = &Asy[dev];
base = asyp->addr;
while(((iir = inportb(base+IIR)) & IIR_IP) == 0){
switch(iir & IIR_ID_MASK){
case IIR_RDA: /* Receiver interrupt */
asyrxint(asyp);
break;
case IIR_THRE: /* Transmit interrupt */
asytxint(dev);
break;
case IIR_MSTAT: /* Modem status change */
asymsint(dev);
asyp->msint_count++;
break;
}
/* should happen at end of a single packet */
if(iir & IIR_FIFO_TIMEOUT)
asyp->fifotimeouts++;
}
}
/* Process 8250 receiver interrupts */
static int
asyrxint(asyp)
struct asy *asyp;
{
register struct fifo *fp;
unsigned base;
char c,lsr;
int cnt = 0;
int trigseen = FALSE;
asyp->rxints++;
base = asyp->addr;
fp = &asyp->fifo;
for(;;){
lsr = inportb(base+LSR);
if(lsr & LSR_OE)
asyp->overrun++;
if(lsr & LSR_DR){
asyp->rxchar++;
c = inportb(base+RBR);
if(asyp->trigchar == uchar(c))
trigseen = TRUE;
/* If buffer is full, we have no choice but
* to drop the character
*/
if(fp->cnt != fp->bufsize){
*fp->wp++ = c;
if(fp->wp >= &fp->buf[fp->bufsize])
/* Wrap around */
fp->wp = fp->buf;
fp->cnt++;
if(fp->cnt > fp->hiwat)
fp->hiwat = fp->cnt;
cnt++;
} else
fp->overrun++;
} else
break;
}
if(cnt > asyp->rxhiwat)
asyp->rxhiwat = cnt;
if(trigseen)
ksignal(fp,1);
return cnt;
}
/* Handle 8250 transmitter interrupts */
static void
asytxint(dev)
int dev;
{
register struct dma *dp;
register unsigned base;
register int count;
struct asy *asyp;
asyp = &Asy[dev];
base = asyp->addr;
dp = &asyp->dma;
asyp->txints++;
if(!dp->flags){
/* "Shouldn't happen", but disable transmit
* interrupts anyway
*/
clrbit(base+IER,IER_TxE);
return; /* Nothing to send */
}
if(!(inportb(base+LSR) & LSR_THRE))
return; /* Not really ready */
/* If it's a 16550A, load up to 16 chars into the tx hw fifo
* at once. With an 8250, it can be one char at most.
*/
if(asyp->is_16550a){
count = min(dp->cnt,OUTPUT_FIFO_SIZE);
/* 16550A: LSR_THRE will drop after the first char loaded
* so we can't look at this bit to determine if the hw fifo is
* full. There seems to be no way to determine if the tx fifo
* is full (any clues?). So we should never get here while the
* fifo isn't empty yet.
*/
asyp->txchar += count;
dp->cnt -= count;
#ifdef notdef /* This is apparently too fast for some chips */
dp->data = outbuf(base+THR,dp->data,count);
#else
while(count-- != 0)
outportb(base+THR,*dp->data++);
#endif
if(dp->cnt == 0){
dp->flags = 0;
/* Disable transmit interrupts */
clrbit(base+IER,IER_TxE);
ksignal(asyp,1);
}
} else { /* 8250 */
do {
asyp->txchar++;
#if defined(HP95)
/* bug work-around, by Ron WA7TAS */
clrbit(base+IER,IER_TxE);
outportb(base+THR,*dp->data++);
setbit(base+IER,IER_TxE);
#else
outportb(base+THR,*dp->data++);
#endif
if(--dp->cnt == 0){
dp->flags = 0;
/* Disable transmit interrupts */
clrbit(base+IER,IER_TxE);
ksignal(asyp,1);
break;
}
} while(inportb(base+LSR) & LSR_THRE);
}
}
/* Handle 8250 modem status change
* If the signals are unchanging, we ignore them.
* If they change, we use them to condition the line.
*/
static void
asymsint(dev)
int dev;
{
struct asy *ap = &Asy[dev];
unsigned base = ap->addr;
ap->msr = inportb(base+MSR);
if ( ap->msr & MSR_CTS ) {
switch ( ap->cts_flow_control ) {
case FOUND_DOWN:
case MOVED_DOWN:
ap->cts_flow_control = MOVED_UP;
/* CTS now asserted, enable Transmit interrupts */
if(ap->dma.flags)
setbit(base+IER,IER_TxE);
break;
};
} else {
switch ( ap->cts_flow_control ) {
case FOUND_UP:
case MOVED_UP:
ap->cts_flow_control = MOVED_DOWN;
/* CTS now dropped, disable Transmit interrupts */
clrbit(base+IER,IER_TxE);
break;
};
}
if ( ap->msr & MSR_RLSD ) {
switch ( ap->rlsd_line_control ) {
case FOUND_DOWN:
case MOVED_DOWN:
ap->rlsd_line_control = MOVED_UP;
/* RLSD just went up */
ksignal( &(ap->rlsd_line_control), 1 );
break;
};
} else {
switch ( ap->rlsd_line_control ) {
case FOUND_UP:
case MOVED_UP:
ap->rlsd_line_control = MOVED_DOWN;
/* RLSD just went down */
ksignal( &(ap->rlsd_line_control), 1 );
break;
};
}
if ( ap->msr & (MSR_TERI | MSR_RI) ) {
asy_ioctl( ap->iface, PARAM_UP, TRUE, 0L );
}
}
/* Wait for a signal that the RLSD modem status has changed */
int
get_rlsd_asy(dev, new_rlsd)
int dev;
int new_rlsd;
{
struct asy *ap = &Asy[dev];
struct iface *ifp = ap->iface;
int result;
if ( ap->rlsd_line_control & IGNORED ) {
return IGNORED;
}
switch ( new_rlsd ) {
case IGNORED:
/* Just return the current value */
return(ap->rlsd_line_control);
case MOVED_DOWN:
if ( !(ap->rlsd_line_control & FOUND_UP) ) {
/* Already at requested value */
return(new_rlsd);
}
break;
case MOVED_UP:
if ( ap->rlsd_line_control & FOUND_UP ) {
/* Already at requested value */
return(new_rlsd);
}
break;
};
/* Wait for state change to requested value */
while (ap->rlsd_line_control != new_rlsd) {
kpause(2L);
kwait( &(ap->rlsd_line_control) );
}
if ( ap->rlsd_line_control & FOUND_UP )
result = PARAM_UP;
else /* DOWN or IGNORED */
result = PARAM_DOWN;
/* set our control signals to follow RLSD */
if ( ifp->ioctl != NULL )
(*ifp->ioctl)( ifp, result, TRUE, 0L );
if ( ifp->iostatus != NULL )
(*ifp->iostatus)( ifp, result, 0L );
return(ap->rlsd_line_control);
}
/* Monitor RLSD signal, and report status changes */
static void
asy_monitor( dev, p1, p2 )
int dev;
void *p1;
void *p2;
{
int save_rlsd = Asy[dev].rlsd_line_control;
while ( save_rlsd != IGNORED ) {
save_rlsd = get_rlsd_asy( dev,
( save_rlsd ^ FOUND_UP ) | MOVED_DOWN );
}
Asy[dev].monitor = NULL;
}
/* Poll the asynch input queues; called on every clock tick.
* This helps limit the interrupt ring buffer occupancy when long
* packets are being received.
*/
void
asytimer()
{
register struct asy *asyp;
register struct fifo *fp;
register int i;
for(i=0;i<ASY_MAX;i++){
asyp = &Asy[i];
fp = &asyp->fifo;
if(fp->cnt != 0)
ksignal(fp,1);
if(asyp->dma.flags != 0 && (inportb(asyp->addr+LSR) & LSR_THRE) \
/* transmit interrupt should be on !,
* ie no flow control */
&& (inportb(asyp->addr+IER) & IER_TxE) ){
asyp->txto++;
asytxint(asyp->iface->dev);
}
#ifdef TIPMAIL
if (Tipsuspended[i].ifp) {
if (!carrier_detect(i)) {
char buf[40];
sprintf (buf, "start tip %s %s %d\n", Tipsuspended[i].ifp->name, (Tipsuspended[i].modem) ? "m" : "t", Tipsuspended[i].timeout);
cmdparse(Cmds,buf,NULL);
Tipsuspended[i].ifp = 0;
Tipsuspended[i].modem = 0;
Tipsuspended[i].timeout = 0;
}
}
#endif
}
}
int
doasystat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct asy *asyp;
for(asyp = Asy;asyp < &Asy[ASY_MAX];asyp++){
if(asyp->iface == NULLIF)
continue;
tprintf("%s:",asyp->iface->name);
if(asyp->is_16550a)
tputs(" [NS16550A]");
if(asyp->trigchar != -1)
tprintf(" [trigger 0x%02x]",asyp->trigchar);
switch (asyp->cts_flow_control) {
case MOVED_DOWN:
case MOVED_UP:
tputs(" [cts flow control]");
break;
};
switch (asyp->rlsd_line_control) {
case MOVED_DOWN:
case MOVED_UP:
tputs(" [rlsd line control]");
break;
case IGNORED:
tputs(" [blind]");
break;
};
tprintf(" %lu bps\n",asyp->speed);
tprintf(" RX: %lu int, %lu chr, %lu hw over, %lu hw hi,",
asyp->rxints,
asyp->rxchar,
asyp->overrun,
asyp->rxhiwat);
asyp->rxhiwat = 0;
if(asyp->is_16550a)
tprintf(" %lu fifo TO,",asyp->fifotimeouts);
tprintf(" %lu sw over, %u sw hi\n",
asyp->fifo.overrun,
asyp->fifo.hiwat);
asyp->fifo.hiwat = 0;
if(tprintf(" TX: %lu int, %lu chr, %u q, %lu MS int, %lu THRE TO\n",
asyp->txints,
asyp->txchar,
len_q(asyp->sndq),
asyp->msint_count,
asyp->txto) == EOF)
break;
}
return 0;
}
/* Send a message on the specified serial line */
int
asy_send(dev,bp)
int dev;
struct mbuf *bp;
{
if(dev < 0 || dev >= ASY_MAX)
return -1;
enqueue(&Asy[dev].sndq,bp);
return 0;
}
/* Serial transmit process, common to all protocols */
static void
asy_tx(dev,p1,p2)
int dev;
void *p1;
void *p2;
{
register struct mbuf *bp;
struct asy *asyp;
struct dma *dp;
int i_state;
asyp = &Asy[dev];
dp = &asyp->dma;
for(;;){
/* Fetch a buffer for transmission */
while(asyp->sndq == NULLBUF)
kwait(&asyp->sndq);
bp = dequeue(&asyp->sndq);
while(bp != NULLBUF){
/* Start the transmitter */
asy_output(dev,bp->data,bp->cnt);
/* Wait for completion */
i_state = disable ();
while(dp->flags == 1)
kwait(asyp);
restore(i_state);
/* Now do next buffer on chain */
bp = free_mbuf(bp);
}
}
}
int
carrier_detect(int dev)
{
struct asy *ap = &Asy[dev];
unsigned base = ap->addr;
ap->msr = inportb(base+MSR);
if ( ap->msr & MSR_RLSD ) {
return 1;
} else {
return 0;
}
}
#endif /* MSDOS */